/**
 * @file DbusHandler.cxx
 * @author libo (rd46@hollyland-tech.com)
 * @brief
 * @version 0.1
 * @date 2023-12-18
 *
 * @copyright Copyright (c) 2023 Shenzhen Hollyland Technology Co.,Ltd
 */
/* ===========================================================================*/
/*  ██╗  ██╗ ██████╗ ██╗     ██╗  ██╗   ██╗██╗      █████╗ ███╗   ██╗██████╗  */
/*  ██║  ██║██╔═══██╗██║     ██║  ╚██╗ ██╔╝██║     ██╔══██╗████╗  ██║██╔══██╗ */
/*  ███████║██║   ██║██║     ██║   ╚████╔╝ ██║     ███████║██╔██╗ ██║██║  ██║ */
/*  ██╔══██║██║   ██║██║     ██║    ╚██╔╝  ██║     ██╔══██║██║╚██╗██║██║  ██║ */
/*  ██║  ██║╚██████╔╝███████╗███████╗██║   ███████╗██║  ██║██║ ╚████║██████╔╝ */
/*  ╚═╝  ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚═╝   ╚══════╝╚═╝  ╚═╝╚═╝  ╚═══╝╚═════╝  */
/*          Copyright (c) 2023 Shenzhen Hollyland Technology Co.,Ltd          */
/* ===========================================================================*/

/* Define to prevent recursive inclusion -------------------------------------*/
/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <mutex>

#include "DbusHandler.hxx"
#include "Log.hxx"
/* define --------------------------------------------------------------------*/
/* typedef -------------------------------------------------------------------*/
/* variables -----------------------------------------------------------------*/
/* Private function(only *.c)  -----------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/*
 * EOF
 */

PATTERN_SINGLETON_IMPLEMENT(CDbusHandler);

CDbusHandler::CDbusHandler()
    : CThread(false, 0, 0, "CDbusHandler")
    , m_dbusConn(NULL)
{
    FUNCTION_ENTER("CDbusHandler");
    m_VcDbusServices.clear();
}

CDbusHandler::~CDbusHandler()
{
    std::cout << "CDbusHandler::~CDbusHandler()" << std::endl;
    if (m_bLoop) {
        DestroyThread(true);
    }
    if (m_dbusConn) {
        dbus_connection_unref(m_dbusConn);
    }
    FUNCTION_ENTER("~CDbusHandler");
    std::cout << "~CDbusHandler done" << std::endl;
}

/**
 * @brief 初始化
 * @param [in] pstrDbusName ：绑定客户端
 * @return true ：成功
 * @return false ：失败
 */
bool CDbusHandler::Init(const char* pstrDbusName)
{
    bool      bRet = false;
    DBusError dbusErr;

    do {
        dbus_error_init(&dbusErr);                             // 初始化DBusError结构体
        m_dbusConn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbusErr);  // 获取消息回话总线
        if (dbus_error_is_set(&dbusErr)) {
            ERR("dbus_bus_get: %s\n", dbusErr.message);
            break;
        }

        // 请求连接总线
        int ret = dbus_bus_request_name(m_dbusConn, pstrDbusName, DBUS_NAME_FLAG_REPLACE_EXISTING, &dbusErr);
        if (dbus_error_is_set(&dbusErr)) {
            ERR("dbus_bus_request_name: %s\n", dbusErr.message);
            break;
        }

        if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
            ERR("Err: DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER\n");
            break;
        }

        bRet = true;
    } while (0);

    dbus_error_free(&dbusErr);  // 释放DBusError结构体
    bRet = CreateThread();
    return bRet;
}
/**
 * @brief 去初始化
 * @return true ：成功
 * @return false ：失败
 */
bool CDbusHandler::CleanUp()
{
    return true;
}

/**
 * @brief 信号发送消息
 * @param [in] pstrObjectName ：发送对象
 * @param [in] pstrInterface ：发送接口
 * @param [in] pstrMethod ：发送信号方法
 * @param [in] strValue ：发送内容
 * @return true ：成功
 * @return false ：失败
 */
bool CDbusHandler::SignalSend(const char* pstrObjectName, const char* pstrInterface, const char* pstrMethod,
                              const char* pstrValue)
{
    if (!m_dbusConn) {
        ERR("m_dbusConn not vaild!");
        return false;
    }

    DBusMessage* msg = NULL;
    if (NULL == pstrObjectName || NULL == pstrInterface || NULL == pstrMethod) {
        ERR("%s param wrong pstrObjectName:%p pstrInterface:%p pstrMethod:%p pstrValue:%p", __func__, pstrObjectName,
            pstrInterface, pstrMethod, pstrValue);
        return false;
    }

    CGuard guard(m_Mutex);

    msg = dbus_message_new_signal(pstrObjectName, pstrInterface, pstrMethod);
    if (!msg) {
        ERR("dbus_message_new_signal Error!");
        return false;
    }

    bool ret = dbus_message_append_args(msg, DBUS_TYPE_STRING, &pstrValue, DBUS_TYPE_INVALID);
    if (!ret) {
        ERR("dbus_message_append_args Error: Out Of Memory!");
        return false;
    }

    ret = dbus_connection_send(m_dbusConn, msg, NULL);
    if (!ret) {
        ERR("dbus_connection_send Error!");
        dbus_message_unref(msg);
        return false;
    }
    dbus_connection_flush(m_dbusConn);
    dbus_message_unref(msg);
    return true;
}

/**
 * @brief 信号监听注册
 * @param [in] pstrInterface ：监听接口
 */
void CDbusHandler::SignalListen(const char* pstrInterface, const char* pstrMethod)
{
    DBusError dbusErr;
    dbus_error_init(&dbusErr);
    char rule[256];
    memset(rule, 0, sizeof(rule));
    sprintf(rule, "type='signal',interface='%s',member='%s'", pstrInterface, pstrMethod);
    dbus_bus_add_match(m_dbusConn, rule, &dbusErr);
    if (dbus_error_is_set(&dbusErr)) {
        ERR("dbus_bus_add_math Error: %s", dbusErr.message);
    }
    dbus_error_free(&dbusErr);
}

/**
 * @brief 方法发送消息(不接收回应)
 * @param [in] pstrDbusDstName ：发送服务器
 * @param [in] pstrObjectName ：发送对象
 * @param [in] pstrInterface ：发送接口
 * @param [in] pstrMethod ：发送方法
 * @param [in] strValue ：发送内容
 * @param [in] pstrReply ：是否需要立即回复 NULL：不需要回复消息 !NULL:获取回复消息到pstrReply
 * @return true ：成功
 * @return false ：失败
 */
bool CDbusHandler::MethodSend(const char* pstrDbusDstName, const char* pstrObjectName, const char* pstrInterface,
                              const char* pstrMethod, const std::string& strValue)
{
    DBusMessage*     msg     = NULL;
    DBusPendingCall* pending = NULL;
    DBusMessageIter  dbusMsgIter;

    if (NULL == pstrObjectName || NULL == pstrInterface || NULL == pstrMethod) {
        ERR("%s param wrong pstrObjectName:%p pstrInterface:%p pstrSignal:%p strValue:%p", __func__, pstrObjectName,
            pstrInterface, pstrMethod, strValue.c_str());
        return false;
    }

    CGuard guard(m_Mutex);

    /* Create Method Call */
    msg = dbus_message_new_method_call(pstrDbusDstName, pstrObjectName, pstrInterface, pstrMethod);
    if (NULL == msg) {
        ERR("dbus_message_new_method_call Error!");
        return false;
    }

    /* Append argument to Method Call*/
    dbus_message_iter_init_append(msg, &dbusMsgIter);
    if (!dbus_message_iter_append_basic(&dbusMsgIter, DBUS_TYPE_STRING, &strValue)) {
        ERR("dbus_message_iter_append_basic: Out of Memory!");
        dbus_message_unref(msg);
        return false;
    }

    /* Send message and flush the connection */
    if (!dbus_connection_send(m_dbusConn, msg, NULL)) {
        ERR("dbus_connection_send: Out of Memory!");
        dbus_message_unref(msg);
        return false;
    }
    dbus_connection_flush(m_dbusConn);

    /* free message and connection */
    if (msg) {
        dbus_message_unref(msg);
    }

    return true;
}

/**
 * @brief 方法发送消息(接收回应)
 * @param [in] pstrDbusDstName ：发送服务器
 * @param [in] pstrObjectName ：发送对象
 * @param [in] pstrInterface ：发送接口
 * @param [in] pstrMethod ：发送方法
 * @param [in] strValue ：发送内容
 * @param [in] strReply ：获取回复消息到strReply
 * @return true ：成功
 * @return false ：失败
 */
bool CDbusHandler::MethodSendGetReply(const char* pstrDbusDstName, const char* pstrObjectName,
                                      const char* pstrInterface, const char* pstrMethod, const std::string& strValue,
                                      std::string& strReply)
{
    char*            reply   = NULL;
    DBusMessage*     msg     = NULL;
    DBusPendingCall* pending = NULL;
    DBusMessageIter  dbusMsgIter;

    if (NULL == pstrObjectName || NULL == pstrInterface || NULL == pstrMethod) {
        ERR("%s param wrong pstrObjectName:%p pstrInterface:%p pstrSignal:%p strValue:%p", __func__, pstrObjectName,
            pstrInterface, pstrMethod, strValue.c_str());
        return false;
    }

    CGuard guard(m_Mutex);

    /* Create Method Call */
    msg = dbus_message_new_method_call(pstrDbusDstName, pstrObjectName, pstrInterface, pstrMethod);
    if (NULL == msg) {
        ERR("dbus_message_new_method_call Error!");
        return false;
    }

    /* Append argument to Method Call*/
    dbus_message_iter_init_append(msg, &dbusMsgIter);
    if (!dbus_message_iter_append_basic(&dbusMsgIter, DBUS_TYPE_STRING, &strValue)) {
        ERR("dbus_message_iter_append_basic: Out of Memory!");
        dbus_message_unref(msg);
        return false;
    }

    DBusMessage* replyMsg = NULL;
    DBusError    dbusErr;

    dbus_error_init(&dbusErr);
    replyMsg = dbus_connection_send_with_reply_and_block(m_dbusConn, msg, -1, &dbusErr);
    if (dbus_error_is_set(&dbusErr)) {
        ERR("Error %s: %s! SendMsg: %s ", dbusErr.name, dbusErr.message, strValue.c_str());
        dbus_message_unref(msg);
        return false;
    }
    dbus_error_free(&dbusErr);

    if (replyMsg) {
        if (!dbus_message_iter_init(replyMsg, &dbusMsgIter)) {
            ERR("Message has no arguments!");
        } else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&dbusMsgIter)) {
            ERR("Argument is not string!");
        } else {
            dbus_message_iter_get_basic(&dbusMsgIter, &reply);
        }
    }

    std::string cstr(reply);
    if (strReply.c_str() && cstr.c_str()) {
        strReply = reply;
    }

    /* free message and connection */
    if (msg) {
        dbus_message_unref(msg);
    }
    return true;
}

/**
 * @brief 检查是否有消息到来
 * @param [in] s32TimeoutMs ：等待时间（ms）
 * @return true ：有消息
 * @return false ：无消息
 */
bool CDbusHandler::QueryMessage(int s32TimeoutMs)
{
    if (dbus_connection_read_write_dispatch(m_dbusConn, s32TimeoutMs)) {
        return true;
    }

    return false;
}

/**
 * @brief 根据注册表接收对应消息并回调到注册函数
 * @param [in] VcDbusServices ：接收对应注册表
 * @param [in] s32ServicesNum ：注册表个数
 * @return true ：成功
 * @return false ：失败
 */
bool CDbusHandler::HandleMessage()
{
    DBusMessage*    reply = NULL;
    DBusMessageIter args;
    std::string     strValue;
    std::string     strParam;
    DBusMessage*    msg   = NULL;
    char*           param = NULL;

    std::unique_lock<std::mutex> Lock(m_Lock);
    if (m_VcDbusServices.empty()) {
        return true;
    }
    auto LocalServices(m_VcDbusServices);
    Lock.unlock();

    /* 从接收队列中获取消息 */
    if (!m_dbusConn) {
        std::cout << "m_dbusConn not vaild!" << std::endl;
        return false;
    }
    msg = dbus_connection_borrow_message(m_dbusConn);
    if (msg == NULL) {
        return true;
    }
    auto MsgType = dbus_message_get_type(msg);
    if (MsgType == DBUS_MESSAGE_TYPE_METHOD_RETURN) { /* 回复消息不处理 */
        dbus_connection_return_message(m_dbusConn, msg);
        return true;
    }
    dbus_connection_steal_borrowed_message(m_dbusConn, msg);
    if (MsgType == DBUS_MESSAGE_TYPE_INVALID) {
        WRN("Recv a invalid msg.");
        dbus_message_unref(msg);
        return false;
    } else if (MsgType == DBUS_MESSAGE_TYPE_ERROR) {
        // WRN("Recv an error msg");
        dbus_message_unref(msg);
        return false;
    }

    /* 处理Signal和Method消息 */
    for (const auto& Service : LocalServices) {
        /* Check whether its the signal we are waiting for */
        if (dbus_message_is_method_call(msg, Service.pstrInterface, Service.pstrMethod)) {
            if (strcmp(dbus_message_get_path(msg), Service.pstrObjectName) == 0) {
                /* read the arguments */
                if (!dbus_message_iter_init(msg, &args)) {
                    fprintf(stderr, "Message has no arguments!\n");
                } else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) {
                    fprintf(stderr, "Argument is not string!\n");
                } else {
                    dbus_message_iter_get_basic(&args, &param);
                }

                // INF("Get method: %s", param);
                std::string cstr(param);
                strValue = Service.pfnInvoke(cstr);

                /* create a reply from the message */
                reply = dbus_message_new_method_return(msg);

                /* add the arguments to the reply */
                dbus_message_iter_init_append(reply, &args);
                if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &strValue)) {
                    ERR("Out Of Memory!");
                    // return false;
                }

                /* send the reply && flush the connection */
                if (!dbus_connection_send(m_dbusConn, reply, NULL)) {
                    ERR("Out Of Memory!");
                    // return false;
                }
                dbus_connection_flush(m_dbusConn);

                // free the reply
                dbus_message_unref(reply);
            }
        } else if (dbus_message_is_signal(msg, Service.pstrInterface, Service.pstrMethod)) {
            DBusError dbusErr;
            dbus_error_init(&dbusErr);
            bool ret = dbus_message_get_args(msg, &dbusErr, DBUS_TYPE_STRING, &param, DBUS_TYPE_INVALID);
            if (!ret) {
                ERR("dbus_message_get_args Error: %s\n", dbusErr.message);
                dbus_error_free(&dbusErr);
                return false;
            }
            INF("Get signal: %s", param);
            std::string cstr(param);
            Service.pfnInvoke(cstr);
            dbus_error_free(&dbusErr);
        }
    }

    if (msg) {
        dbus_message_unref(msg);
    }

    return true;
}

/**
 * @brief 注册回调函数
 * @param [in] pstrObjectName ：接受对象
 * @param [in] pstrtInterface ：接受接口
 * @param [in] pstrMethod ：接受方法
 * @param [in] pfnRegistrationCallback ：回调消息处理函数（不允许阻塞和耗时操作）
 * @param [in] isSingnal :是否为信号接收函数（默认：false）
 * @return true 成功
 * @return false 失败
 */
bool CDbusHandler::RegisteredServices(const char* pstrObjectName, const char* pstrtInterface, const char* pstrMethod,
                                      const std::string (*pfnRegistrationCallback)(const std::string& pstrValue),
                                      bool isSingnal)
{
    DBUS_SERVICES VcDbusServices;
    if (pstrObjectName == NULL || pstrtInterface == NULL || pstrMethod == NULL || pfnRegistrationCallback == NULL) {
        ERR("%s param wrong pstrObjectName:%p pstrtInterface:%p pstrMethod:%p pfnRegistrationCallback:%p", __func__,
            pstrObjectName, pstrtInterface, pstrMethod, pfnRegistrationCallback);
        return false;
    }

    if (isSingnal) {
        g_DbusHandler.SignalListen(pstrtInterface, pstrMethod);
    }

    VcDbusServices.pstrObjectName = pstrObjectName;
    VcDbusServices.pstrInterface  = pstrtInterface;
    VcDbusServices.pstrMethod     = pstrMethod;
    VcDbusServices.pfnInvoke      = pfnRegistrationCallback;
    std::lock_guard<std::mutex> Guard(m_Lock);
    m_VcDbusServices.push_back(VcDbusServices);
    return true;
}

/**
 * @brief 反注册
 * @param [in] pstrObjectName ：接受对象
 * @param [in] pstrtInterface ：接受接口
 * @param [in] pstrMethod ：接受方法
 * @return true 成功
 * @return false 失败
 */
bool CDbusHandler::UnRegisteredServices(const char* pstrObjectName, const char* pstrtInterface, const char* pstrMethod)
{
    if (pstrObjectName == NULL || pstrtInterface == NULL || pstrMethod == NULL) {
        ERR("%s param wrong pstrObjectName:%p pstrtInterface:%p pstrMethod:%p ", __func__, pstrObjectName,
            pstrtInterface, pstrMethod);
        return false;
    }

    std::lock_guard<std::mutex> Guard(m_Lock);

    for (auto iter = m_VcDbusServices.begin(); iter != m_VcDbusServices.end();) {
        if (strcmp((*iter).pstrObjectName, pstrObjectName) == 0 && strcmp((*iter).pstrInterface, pstrObjectName)
            && strcmp((*iter).pstrMethod, pstrMethod)) {
            iter = m_VcDbusServices.erase(iter);
        } else {
            iter++;
        }
    }
    return true;
}

void CDbusHandler::ThreadProc()
{
    DBG("Enter CDbusHandler Thread, thread PID = %d, PPID = %d.", GetThreadID(), getppid());
    while (m_bLoop) {
        if (QueryMessage()) {
            HandleMessage();
        } else {
            XSleep(10);
        }
    }
}

